<?php

namespace App\Services\Pay;

use App\Models\Common\PayWay;
use App\Services\Com\LogService;
use Carbon\Carbon;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Cache;
use Psr\Http\Message\ResponseInterface;

class PayService
{
    private $http          = null;
    private $host          = null;
    private $token         = null;
    private $client_id     = null;
    private $client_secret = null;
    private $store_no      = null;

    public function __construct()
    {
        $this->host          = trim(env('PAY_SERVER_HOST'), '/');
        $this->http          = new Client([
            'base_uri' => $this->host,
        ]);
        $this->client_id     = env('PAY_SERVER_CLIENT_ID');
        $this->client_secret = env('PAY_SERVER_CLIENT_SECRET');
        $this->store_no      = env('PAY_SERVER_STORE_NO');
    }

    /**
     * 发起 公众号 支付
     *
     * @param $gateway
     * @param $trade_no
     * @param $subject
     * @param $total_amount
     * @param $open_id
     *
     * @return mixed|\Psr\Http\Message\ResponseInterface
     * @throws \Exception
     */
    public function mpPay($gateway, $trade_no, $subject, $total_amount, $open_id)
    {
        $this->login();
        $data = [
            'gateway' => $gateway,
            'store' => $this->store_no,
            'trade_no' => $trade_no,
            'subject' => $subject,
            'total_amount' => $total_amount,
            'openid' => $open_id,
        ];

        try {
            $response = $this->http->post($this->host . '/api/pay/mp', [
                'form_params' => $data,
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => $this->token,
                ]
            ]);
            $response = json_decode((string)$response->getBody(), true);

            if (!isset($response['success']) || !$response['success'])
            {
                LogService::error('PAY SERVER ERROR -> '.print_r($response, true));
                throw new Exception($response['message'] ?? '支付不成功！');
            }

            return $response['message'];
        } catch (\Exception $e) {
            switch ((int) $e->getCode())
            {
                case 401:
                    throw new Exception('支付客户端鉴定失败'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
                default:
                    LogService::error('Oauth error: '.$e->getMessage());
                    throw new Exception('支付服务端发生错误'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
            }
        }
    }
    /**
     * 发起 小程序 支付
     *
     * @param  string       $gateway
     * @param  string       $trade_no
     * @param  string       $subject
     * @param  int          $total_amount
     * @param  string       $open_id
     *
     * @param  string|null  $appid
     *
     * @return mixed|ResponseInterface
     * @throws Exception
     */
    public function miniAppPay(string $gateway, string $trade_no, string $subject, int $total_amount, string $open_id, string $appid = null)
    {
        $this->login();
        $data = [
            'gateway'      => $gateway,
            'store'        => $appid ?: $this->store_no,
            'trade_no'     => $trade_no,
            'subject'      => $subject,
            'total_amount' => $total_amount,
            'openid'       => $open_id,
        ];

        try
        {
            $response = $this->http->post('/api/pay/miniapp', [
                'form_params' => $data,
                'headers'     => [
                    'Accept'        => 'application/json',
                    'Authorization' => $this->token,
                ],
            ]);
            $response = json_decode((string) $response->getBody(), true);

            if (!isset($response['success']) || !$response['success'])
            {
                LogService::error('PAY SERVER ERROR -> '.print_r($response, true));
                throw new Exception($response['message'] ?? '支付不成功！');
            }

            return $response['message'];
        }
        catch (Exception $e)
        {
            switch ((int) $e->getCode())
            {
                case 401:
                    throw new Exception('支付客户端鉴定失败'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
                default:
                    LogService::error('Oauth error: '.$e->getMessage());
                    throw new Exception('支付服务端发生错误'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
            }
        }
    }

    /**
     * 发起 APP 支付
     *
     * @param $gateway
     * @param $trade_no
     * @param $subject
     * @param $total_amount
     * @return mixed|\Psr\Http\Message\ResponseInterface
     * @throws \Exception
     */
    public function appPay($gateway, $trade_no, $subject, $total_amount)
    {
        $this->login();
        $data = [
            'gateway' => $gateway,
            'store' => $this->store_no,
            'trade_no' => $trade_no,
            'subject' => $subject,
            'total_amount' => $total_amount,
        ];

        try {
            $response = $this->http->post($this->host . '/api/pay/app', [
                'form_params' => $data,
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => $this->token,
                ]
            ]);
            $response = json_decode((string)$response->getBody(), true);

            if (!isset($response['success']) || !$response['success']) {
                LogService::error('PAY SERVER ERROR -> ' . print_r($response, true));
                throw new \Exception($response['message'] ?? '支付不成功！');
            }

            return $response['message'];
        } catch (\Exception $e) {
            switch ((int)$e->getCode()) {
                case 401:
                    throw new \Exception('支付客户端鉴定失败' . env('APP_DEBUG') ? $e->getMessage() : '', (int)$e->getCode());
                default:
                    LogService::error('Oauth error: ' . $e->getMessage());
                    throw new \Exception('支付服务端发生错误' . env('APP_DEBUG') ? $e->getMessage() : '', (int)$e->getCode());
            }
        }
    }


    /**
     * 退款
     *
     * @param          $gateway wechat
     * @param          $trade_no  支付订单号
     * @param          $refund_no
     * @param          $desc  备注
     * @param          $refund_amount  退款金额
     *
     * @return mixed
     * @throws Exception
     */
    public function refund(string $gateway, string $trade_no, string $refund_no, string $desc, int $refund_amount)
    {
        $this->login();
        $data = [
            'gateway'       => $gateway, // 冗余传输
            'store'         => $this->store_no,
            'trade_no'      => $trade_no,
            'refund_no'     => $refund_no,
            'desc'          => $desc,
            'refund_amount' => $refund_amount,
        ];

        try
        {
            $response = $this->http->post('/api/order/refund', [
                'form_params' => $data,
                'headers'     => [
                    'Accept'        => 'application/json',
                    'Authorization' => $this->token ,
                ],
            ]);
            return json_decode((string) $response->getBody(), true);
        }
        catch (Exception $e)
        {
            switch ((int) $e->getCode())
            {
                case 401:
                    throw new Exception('支付客户端鉴定失败'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
                default:
                    throw new Exception('支付服务端发生错误'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
            }
        }
    }


    /**
     * 查询交易订单
     *
     * @param $store_id
     * @param $trade_no
     *
     * @return mixed
     * @throws Exception
     */
    public function query($trade_no)
    {
        $this->login();

        try
        {
            $response = $this->http->get("/api/order/query/{$this->store_no}/{$trade_no}", [
                'headers' => [
                    'Accept'        => 'application/json',
                    'Authorization' => $this->token,
                ],
            ]);

            $response = json_decode((string)$response->getBody(), true);

            if (!isset($response['success']) || !$response['success']) {
                LogService::error('PAY SERVER ERROR -> ' . print_r($response, true));
                throw new \Exception($response['message'] ?? '查询不成功！');
            }

            return $response['message'];
        }
        catch (Exception $e)
        {
            switch ((int) $e->getCode())
            {
                case 401:
                    throw new Exception('支付客户端鉴定失败'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
                default:
                    throw new Exception('支付服务端发生错误'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
            }
        }
    }

    /**
     * 登录客户端认证
     *
     * @return bool
     * @throws Exception
     */
    public function login()
    {
        $key = md5($this->host);

        if (Cache::has($key))
        {
            $this->token = Cache::get($key);

            return true;
        }

        $data = [
            'grant_type'    => 'client_credentials',
            'client_id'     => $this->client_id,
            'client_secret' => $this->client_secret,
        ];

        try
        {
            $response = $this->http->post('/api/oauth/token', [
                'form_params' => $data,
            ]);
            $response = json_decode((string) $response->getBody(), true);

            $type        = $response['token_type'];
            $expire      = $response['expires_in'];
            $token       = $response['access_token'];
            $this->token = $type.' '.$token;

            Cache::put($key, $this->token, Carbon::now()->addSeconds($expire));

            return true;
        }
        catch (Exception $e)
        {
            switch ((int) $e->getCode())
            {
                case 401:
                    throw new Exception('支付客户端鉴定失败'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
                default:
                    throw new Exception('支付服务端发生错误'.env('APP_DEBUG') ? $e->getMessage() : '', (int) $e->getCode());
            }
        }
    }

    //获取支付方式
    public static function payWay($key){
        return PayWay::find($key)->code;
    }
}
